package com.geek.rest;

import com.geek.Assert;

import javax.ws.rs.BeanParam;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.UriBuilder;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.ServiceLoader;


public class RestClientInvocationHandler implements InvocationHandler {

    private final Client client;

    private final Map<Method, RequestTemplate> requestTemplateMap;

    private final Iterable<UriAssembler> uriAssemblers;

    public RestClientInvocationHandler(Configuration configuration, Map<Method, RequestTemplate> requestTemplateMap) {
        this.client = ClientBuilder.newClient(configuration);
        this.requestTemplateMap = requestTemplateMap;
        uriAssemblers = initUriAssembler();
    }

    private  Iterable<UriAssembler> initUriAssembler() {
        return ServiceLoader.load(UriAssembler.class);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RequestTemplate requestTemplate = requestTemplateMap.get(method);
        Assert.notNull(requestTemplate, "方法不存在");

        //原始的uri
        UriBuilder uriBuilder = UriBuilder.fromUri(requestTemplate.getUriTemplate());

        //将请求的uri拼装好
        for (UriAssembler uriAssembler : uriAssemblers) {
            uriAssembler.assembler(uriBuilder, requestTemplate, args);
        }

        Entity<?> entity = buildRequestEntity(requestTemplate, method, args);

        Invocation build = client.target(uriBuilder)
                .request(requestTemplate.getProduces().toArray(new String[0]))
                .build(requestTemplate.getMethod(), entity);

        return build.invoke(method.getReturnType());
    }

    /**
     * 获取方法参数中包含 {@link javax.ws.rs.BeanParam}的参数
     * @param requestTemplate
     * @param method
     * @param args
     * @return
     */
    private Entity<?> buildRequestEntity(RequestTemplate requestTemplate, Method method, Object[] args) {
        int beanParamIndex = -1;
        Annotation[][] annotations = method.getParameterAnnotations();
        for (int i = 0; i < annotations.length; i++) {
            Annotation[] annos = annotations[i];
            for (Annotation anno : annos) {
                if (BeanParam.class.equals(anno.annotationType())) {
                    beanParamIndex = i;
                    break;
                }
            }
        }

        if (beanParamIndex == -1) {
            return null;
        }
        return Entity.entity(args[beanParamIndex], requestTemplate.getMediaType());
    }

}
